package com.sky.aspect;

import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * @author zzw
 * @date 2025/5/20
 * @description 自定义切面，实现公共字段自动填充功能
 */
@Slf4j
@Aspect
@Component
public class AutoFillAspect {

    /**
     * 切入点
     */
    @Pointcut("execution(* com.sky.mapper.*.*(..))&&@annotation(com.sky.annotation.AutoFill)")
    public void autoFillPointCut() {
    }

    /**
     * 前置通知，在方法执行前进行填充
     *
     * @param joinPoint 切入点
     */
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint) {
        log.info("开始进行公共字段填充");

        // 1.获取方法签名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        // 2.获取方法上的注解
        AutoFill autoFill = methodSignature.getMethod().getAnnotation(AutoFill.class);

        // 3.获取注解的值(操作类型)
        OperationType value = autoFill.value();

        // 4.获取当前方法参数, 获取第一个参数
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) {
            return;
        }
        Object entity = args[0];
        log.info("当前对象：{}", entity);

        LocalDateTime now = LocalDateTime.now();
        Long empId = BaseContext.getCurrentId();

        // 5.根据对应的注解值，完成公共字段的填充
        if (value == OperationType.INSERT) {
            handleInsertOperation(entity, now, empId);
        } else if (value == OperationType.UPDATE) {
            handleUpdateOperation(entity, now, empId);
        }
    }

    /**
     * 处理插入操作的公共字段填充
     *
     * @param entity 目标实体对象
     * @param now    当前时间
     * @param empId  当前员工ID
     */
    private void handleInsertOperation(Object entity, LocalDateTime now, Long empId) {
        // 设置创建时间
        invokeSetterMethod(entity, AutoFillConstant.SET_CREATE_TIME, now);
        // 设置创建人
        invokeSetterMethod(entity, AutoFillConstant.SET_CREATE_USER, empId);
        // 插入操作也需要设置更新字段
        handleUpdateOperation(entity, now, empId);
    }

    /**
     * 处理更新操作的公共字段填充
     *
     * @param entity 目标实体对象
     * @param now    当前时间
     * @param empId  当前员工ID
     */
    private void handleUpdateOperation(Object entity, LocalDateTime now, Long empId) {
        // 设置更新时间
        invokeSetterMethod(entity, AutoFillConstant.SET_UPDATE_TIME, now);
        // 设置更新人
        invokeSetterMethod(entity, AutoFillConstant.SET_UPDATE_USER, empId);
    }

    /**
     * 反射调用对象的setter方法
     *
     * @param target     目标对象
     * @param methodName 方法名
     * @param value      要设置的值
     */
    private void invokeSetterMethod(Object target, String methodName, Object value) {
        try {
            Class<?> paramType = value.getClass();
            Method method = target.getClass().getDeclaredMethod(methodName, paramType);
            method.invoke(target, value);
        }
        catch (Exception e) {
            log.error("反射调用方法[{}]失败: {}", methodName, e.getMessage());
        }
    }
}